package com.ybg.bookrules.services.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ybg.bookrules.commons.BoomValication;
import com.ybg.bookrules.mapper.UserinfosMapper;
import com.ybg.bookrules.module.dto.MenusDTO;
import com.ybg.bookrules.module.dto.UserinfosDTO;
import com.ybg.bookrules.module.entity.Menus;
import com.ybg.bookrules.module.entity.Userinfos;
import com.ybg.bookrules.module.vo.UserinfosVO;
import com.ybg.bookrules.services.MenusService;
import com.ybg.bookrules.services.UserinfosService;
import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.Date;
import java.util.List;
import java.util.Random;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
public class UserinfosServiceImpl extends ServiceImpl<UserinfosMapper, Userinfos>
        implements UserinfosService {
    @Resource
    private MenusService menusService;
    @Resource
    private StringRedisTemplate template;
    @Value("${redis.rule}")
    private String bigKey;

    @Resource
    private BoomValication boomValication;
    @Resource
    private BoomValication nameBoom;

    @PostConstruct
    public void initBoom(){
        //将所有的用户和密码存放到boomValication过滤器中 将用户名存放到nameBoom过滤器中
        this.getBaseMapper().selectList(null)
                .stream().forEach(u->{
                    boomValication.put(u.getUsername()+"_"+u.getPassword());
                    nameBoom.put(u.getUsername());
                });
    }


    /**
     * 登录服务
     * @param username
     * @param password
     * @return
     */
    @Override
    public UserinfosDTO userLogin(String username, String password) {

        //1.接收用户的用户名和密码 先对密码进行MD5加密
        String newPwd = DigestUtils.md5Hex(password);
        //1.1 优先搜索redis数据库 如果用户存在则直接返回用户信息
        String smallKey= username+"_"+newPwd;
        Object res = template.opsForHash().get(bigKey, smallKey);
        //1.2生成Token
        String token =UUID.randomUUID().toString().replaceAll("-","");
        System.out.println(token);
        if(res==null){
            //使用boom过滤器检查是否有用户输入的对象 如果没有则直接返回null 如果有再去查数据库
            if(boomValication.exists(smallKey)) {
                //2.使用QueryWrapper对用户名和密码查询进行查询条件封装
                QueryWrapper<Userinfos> userQuery = new QueryWrapper<>();
                userQuery.eq("username", username).eq("password", newPwd);

                //3.查询的用户对象中获取用户的userid
                Userinfos user = this.getBaseMapper().selectOne(userQuery);
                if (user != null) {
                    //4.使用用户id到MenusMapper中？MenusService中查询对应的菜单
                    List<Menus> menus = menusService.selectMenusByUserid(user.getUserid());
                    //5.将菜单和用户信息存放到UserinfosDTO 并返回
                    //5.1将List<Menus>---->List<MenusDTO>
                    List<MenusDTO> mdto = menus.stream().map(menu -> MenusDTO.builder()
                                    .menuid(menu.getMenuid()).menuname(menu.getMenuname()).build())
                            .collect(Collectors.toList());
                    //5.2将Userinfos对象中的属性复制到UserinfosDTO对象下 同时生成token
                    UserinfosDTO usdto = UserinfosDTO.builder().menus(mdto)
                            .token(token)
                            .build();
                    BeanUtils.copyProperties(user, usdto);
                    //5.3 将用户名和token 存放到缓存数据库中 并设置失效时间

                    template.opsForValue().set(token,usdto.toString(),30+ new Random().nextInt(70), TimeUnit.MINUTES);
                    return usdto;
                } else {
                    //未找到对应的用户 则返回null
                    System.out.println("mysql 未找到用户");
                    return null;
                }
            } else{
                System.out.println("boom过滤器 未找到用户");
                return null;
            }
        }else{
            //使用缓存服务器上的用户信息进行登录
            String [] infos = res.toString().split(",");
            UserinfosDTO udto = UserinfosDTO.builder()
                    .userid(Integer.parseInt(infos[0]))
                    .username(infos[2])
                    .realname(infos[3])
                    .gender(Integer.parseInt(infos[4]))
                    .cardid(infos[5])
                    .address(infos[6])
                    .telephone(infos[7])
                    .token(token)
                    .build();
            //5.3 将用户名和token 存放到缓存数据库中 并设置失效时间
            template.opsForValue().set(token,udto.toString(),30+new Random().nextInt(70), TimeUnit.MINUTES);
            return udto;
        }
    }

    /**
     * 用户名验证,验证用户是否存在 用户不存在 则可以注册
     */
    public boolean existsUserByUsername(String username) {
        return nameBoom.exists(username);
    }

    /**
     * 用户注册
     */
    @Transactional
    public boolean userRegister(UserinfosVO uvo){
        try {
            //将UserinfosVO转为Userinfos
            Userinfos us = new Userinfos();
            BeanUtils.copyProperties(uvo,us);
            us.setRoleid(2);//只能注册普通会员用户
            us.setPassword(DigestUtils.md5Hex(us.getPassword()));//用户密码MD5加密
            us.setRegistertime(new Date());
            us.setDeleted(0);
            //将对象保存到mysql数据库
//        this.getBaseMapper().insert(us);
            this.save(us);
            //将该用户存放到缓存数据库中
            //userid, roleid, username, realname, gender, cardid, address, telephone, registertime
            template.opsForHash().put(bigKey,
                    us.getUsername()+"_"+us.getPassword(),
                            us.toString()
                    );
            //同时将用户名和密码存放到两个boomfilter中
            boomValication.put(us.getUsername()+"_"+us.getPassword());
            nameBoom.put(us.getUsername());
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

}

